use core::f64;
use std::collections::HashMap;

use anyhow::Result;
use chrono::DateTime;
use chrono::Local;
use chrono::Utc;
use fudata::db;
use fudata::db::malines::sma::Sma;
use fudata::db::malines::smpr::Smpr;
use fudata::model::kline::Klt;
use fudata::model::kline::Ohlcv;
use fudata::model::malines::ma::MAConfig;
use fudata::model::malines::ma::MA;
use fudata::model::security;
use fudata::model::security::Market;
use fudata::model::trade::trade_storder::KlineLowest;
use fudata::round2;

use fudata::model::kline::Kline;
use futures::StreamExt;
use futures::stream::FuturesUnordered;
use futures::stream::{ self };
use log::info;
use log::trace;
use surrealdb::sql::Thing;

use crate::todo::MAX_KLINE_DAYS;
use crate::todo::MAX_STK_SECURITY;

/// Calculate the mean of a list of numbers.
fn mean(numbers: &[f64]) -> f64 {
    numbers.iter().sum::<f64>() / (numbers.len() as f64)
}

/// 计算数据集的方差
fn variance(numbers: &[f64]) -> f64 {
    let m = mean(numbers);
    let n = numbers.len();
    numbers
        .iter()
        .map(|&x| ((x as f64) - m).powi(2))
        .sum::<f64>() /
        ((n as f64) - 1.0)
}

/// 计算数据集的标准差
fn standard_deviation(numbers: &[f64]) -> f64 {
    variance(numbers).sqrt()
}

/// Calculate the absolute deviation of a given number from the mean.
fn absolute_deviation_from_mean(number: f64, numbers: &[f64]) -> f64 {
    let m = mean(numbers);
    number - m
}

/// Calculate the percentage deviation of a given number from the mean.
fn percentage_deviation_from_mean(number: f64, numbers: &[f64]) -> f64 {
    let m = mean(numbers);
    ((number - m) / m) * 100.0
}

/// Finds the rank of the target value in the vector.
/// If the target is less than all values, returns i32::MIN.
/// If the target is greater than all values, returns i32::MAX.
/// Otherwise, returns the index of the first value greater than the target.
fn find_rank(values: &mut Vec<f64>, target: f64) -> i32 {
    // Sort the vector to make searching easier
    values.sort_by(|a, b| a.partial_cmp(b).unwrap());

    // Find the rank
    match values.iter().position(|&x| x > target) {
        Some(index) => index as i32,
        None => {
            if values.is_empty() || target < *values.first().unwrap() { i32::MIN } else { i32::MAX }
        }
    }
}

/// Finds the highest and lowest Klines for each {period}.
///
/// # Arguments
/// * `all_klines` - A vector of Kline objects.
/// * `period` - The number of trading days to consider for each period.
///
/// # Returns
/// A vector of tuples, where each tuple contains the highest and lowest Kline for each period.
///
/// # Examples
/// ```
/// let all_klines = vec![/* populate with Kline instances */];
/// let period = 20;
/// let result = find_high_low_klines(&all_klines, period);
/// ```
pub fn find_high_low_klines(
    all_klines: &Vec<Kline>,
    before_klts: &DateTime<Utc>,
    period: u16
) -> Vec<(Kline, Kline)> {
    let mut result = Vec::new();
    let mut current_period_start = 0;

    let empty_kline = Kline { high: f64::MIN, low: f64::MAX, ..Default::default() };
    while current_period_start < all_klines.len() {
        let end_index = std::cmp::min(current_period_start + (period as usize), all_klines.len());
        let sub_klines = &all_klines[current_period_start..end_index];

        let mut max_kline = Some(&empty_kline);
        let mut min_kline = Some(&empty_kline);
        for kline in sub_klines.iter() {
            if kline.klts < *before_klts {
                if let (Some(maxkl), Some(minkl)) = (max_kline, min_kline) {
                    if kline.high > maxkl.high {
                        max_kline = Some(kline);
                    }
                    if kline.low < minkl.low {
                        min_kline = Some(kline);
                    }
                }
            }
        }

        if let (Some(maxkl), Some(minkl)) = (max_kline, min_kline) {
            if maxkl.high > f64::MIN && minkl.low < f64::MAX {
                result.push((maxkl.clone(), minkl.clone()));
            }
        }

        current_period_start += period as usize;
    }

    result
}

/// Finds up to `lowest_count` Kline objects with the lowest `low` values
/// within the date range defined by `start_klts` and `end_klts`.
pub fn find_lowest_klines(
    before_klts: &DateTime<Utc>,
    all_klines: &Vec<Kline>,
    span_day: &(i64, i64),
    lowest_count: u16
) -> Vec<Kline> {
    let start_time = Local::now();
    let start_klts = before_klts.clone() - chrono::Duration::days(span_day.1);
    let end_klts = before_klts.clone() - chrono::Duration::days(span_day.0);

    // Filter the Kline objects based on the klts being within the date range
    let filtered_klines: Vec<&Kline> = all_klines
        .iter()
        .filter(|k| start_klts <= k.klts && k.klts < end_klts)
        .collect();

    // Sort the filtered Kline objects by their low value
    let mut sorted_klines: Vec<&Kline> = filtered_klines.into_iter().collect();
    sorted_klines.sort_by_key(|k| (k.low * 100.0) as i64);

    // Collect the top `lowest_count` Kline objects
    let lkls = sorted_klines
        .into_iter()
        .take(lowest_count as usize)
        .map(|k| k.clone())
        .collect();

    let spent_time = Local::now().signed_duration_since(start_time);
    trace!(
        "find_lowest_klines  spent seconds={}.{} ",
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );

    lkls
}

/// Finds up to `toppest_count` Kline objects with the highest `high` values
/// within the date range defined by `start_klts` and `end_klts`.
pub fn find_highest_klines(
    before_klts: &DateTime<Utc>,
    all_klines: &Vec<Kline>,
    span_day: &(i64, i64),
    highest_count: u16
) -> Vec<Kline> {
    let start_time = Local::now();
    let start_klts = before_klts.clone() - chrono::Duration::days(span_day.1);
    let end_klts = before_klts.clone() - chrono::Duration::days(span_day.0);

    // Filter the Kline objects based on the klts being within the date range
    let filtered_klines: Vec<&Kline> = all_klines
        .iter()
        .filter(|k| start_klts <= k.klts && k.klts < end_klts)
        .collect();

    // Sort the filtered Kline objects by their low value
    let mut sorted_klines: Vec<&Kline> = filtered_klines.into_iter().collect();
    sorted_klines.sort_by(|a, b| {
        // Compare in reverse order
        b.high.partial_cmp(&a.high).unwrap()
    });

    // Collect the top `lowest_count` Kline objects
    let hkls = sorted_klines
        .into_iter()
        .take(highest_count as usize)
        .map(|k| k.clone())
        .collect();

    let spent_time = Local::now().signed_duration_since(start_time);
    trace!(
        "find_highest_klines  spent seconds={}.{} ",
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );
    hkls
}

/// 计算一定时间跨度内Ohlcv所有字段的平均值
pub fn compute_ohlcv_averages(
    before_klts: &DateTime<Utc>,
    all_klines: &Vec<Kline>,
    span_day: &(i64, i64)
) -> Option<Ohlcv> {
    let start_time = Local::now();
    if all_klines.is_empty() {
        return None;
    }
    let start_klts = before_klts.clone() - chrono::Duration::days(span_day.1);
    let end_klts = before_klts.clone() - chrono::Duration::days(span_day.0);

    // Filter the Kline objects based on the klts being within the date range
    let klines: Vec<&Kline> = all_klines
        .iter()
        .filter(|k| start_klts <= k.klts && k.klts < end_klts)
        .collect();

    let oo = if klines.len() > 0 {
        let n = klines.len() as f64;

        let sum_o = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().o);
        let sum_c = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().c);
        let sum_h = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().h);
        let sum_l = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().l);
        let sum_value = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().value);
        let sum_amplitude = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().amplitude);
        let sum_fluctuation_rate = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().frate);
        let sum_fluctuation_amount = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().famount);
        let sum_turnover_rate = klines.iter().fold(0.0, |acc, kl| acc + kl.ohlcv().trate);

        let avg_v = (klines.iter().fold(0, |acc, kl| acc + (kl.ohlcv().v as u64)) as f64) / n;

        Some(Ohlcv {
            o: round2(sum_o / n),
            c: round2(sum_c / n),
            h: round2(sum_h / n),
            l: round2(sum_l / n),
            v: avg_v as u64,
            value: round2(sum_value / n),
            amplitude: round2(sum_amplitude / n),
            frate: round2(sum_fluctuation_rate / n),
            famount: round2(sum_fluctuation_amount / n),
            trate: round2(sum_turnover_rate / n),
        })
    } else {
        None
    };

    let spent_time = Local::now().signed_duration_since(start_time);
    trace!(
        "compute_ohlcv_averages  spent seconds={}.{} ",
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );

    oo
}

pub fn calculate_kline_low_deviation(
    security_id: &str,
    kline: &Kline,
    span_day: &(i64, i64),
    all_klines: &Vec<Kline>,
    lowest_count: u16
) -> KlineLowest {
    let start_time = Local::now();
    let ohlcv = kline.ohlcv();
    let kline_id = kline.id.clone().unwrap().id.to_raw();

    let kls = find_lowest_klines(&kline.klts, all_klines, span_day, lowest_count);

    let mut lowest_lows: Vec<f64> = vec![];
    for kl in kls {
        lowest_lows.push(kl.low);
    }

    let rank = find_rank(&mut lowest_lows, ohlcv.l);
    let mean_val = round2(mean(&lowest_lows));
    let variance = round2(variance(&lowest_lows));
    let std_deviation = round2(standard_deviation(&lowest_lows));
    let abs_deviation = round2(absolute_deviation_from_mean(ohlcv.l, &lowest_lows));
    let pct_deviation = round2(percentage_deviation_from_mean(ohlcv.l, &lowest_lows));

    let result = KlineLowest {
        ohlcv,
        rank,
        lowest_lows,
        mean: mean_val,
        variance,
        std_deviation,
        abs_deviation,
        pct_deviation,
    };

    let spent_time = Local::now().signed_duration_since(start_time);
    info!(
        "calculate_kline_low_deviation  spent seconds={}.{} ",
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );

    result
}

pub async fn calculate_latest_klines_deviation(
    security_id: String,
    klt: Klt,
    klines_number: i64,
    lowest_span_day: &(i64, i64),
    lowest_klt: Klt,
    lowest_klines_count: u16,
    work_no: String
) -> Result<Vec<KlineLowest>> {
    let start_time = Local::now();

    let mut result = Vec::new();
    let klines = db::kline::select(
        &security_id,
        None,
        Some(&klt),
        Some("klts DESC"),
        0,
        klines_number as u16
    ).await?;

    let all_klines = db::kline::select(
        &security_id,
        None,
        Some(&lowest_klt),
        Some("klts ASC"),
        0,
        MAX_KLINE_DAYS as u16
    ).await?;

    for (idx, kline) in klines.iter().enumerate() {
        let kld = calculate_kline_low_deviation(
            &security_id,
            kline,
            lowest_span_day,
            &all_klines,
            lowest_klines_count
        );
        result.push(kld);
    }

    let spent_time = Local::now().signed_duration_since(start_time);
    info!(
        "calculate_latest_klines_deviation@{work_no} {security_id} {} klines spent={}.{}s ",
        klines.len(),
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );

    Ok(result)
}

// The main function to process all securities.
pub async fn compute_securities_kline_deviations(
    market: &Option<Market>,
    type_: Option<security::SecurityType>,
    klt: Klt,
    klines_number: i64,
    lowest_span_day: &(i64, i64),
    lowest_klt: Klt,
    lowest_klines_count: u16,
    pi: u16,
    pn: u16
) -> Result<usize> {
    let start_time = Local::now();

    let securities = db::security::select(market, type_, None, Some("id ASC"), pi, pn).await?;
    let total = securities.len();

    // Create a vector of boxed and pinned futures.
    let security_futures: Vec<_> = securities
        .into_iter()
        .enumerate()
        .map(|(idx, security)| {
            let security_id = security.id.clone().unwrap().id.to_raw();
            let work_no = format!("{}/{}", idx, total);
            Box::pin(
                calculate_latest_klines_deviation(
                    security_id,
                    klt,
                    klines_number,
                    lowest_span_day,
                    lowest_klt,
                    lowest_klines_count,
                    work_no
                )
            )
        })
        .collect();

    // Use FuturesUnordered to handle the futures concurrently.
    let mut deviations_futures: FuturesUnordered<_> = security_futures.into_iter().collect();

    let mut kld_count = 0;
    // Collect the results into a VecDeque so we can sort them later.
    while let Some(Ok(deviations)) = deviations_futures.next().await {
        kld_count += deviations.len();
        println!("(lowest_lines::compute_securities_kline_deviations) ");
    }

    let spent_time = Local::now().signed_duration_since(start_time);
    info!(
        "compute_securities_kline_deviations {kld_count} kline_deviations for {total} securities, spent={}.{}s ",
        spent_time.num_seconds(),
        spent_time.num_milliseconds() % 1000
    );

    Ok(kld_count)
}

#[cfg(test)]
mod test {
    use std::vec;

    use chrono::Duration;
    use chrono::Utc;
    use fudata::db;
    use fudata::model::kline::Kline;
    use fudata::model::kline::Klt;
    use fudata::model::security::Market;
    use fudata::model::security::SecurityType;
    use fudata::round2;
    use log::debug;
    use log::info;

    use crate::qtrade::strategy::util::highlow_klines::absolute_deviation_from_mean;
    use crate::qtrade::strategy::util::highlow_klines::calculate_latest_klines_deviation;
    use crate::qtrade::strategy::util::highlow_klines::find_high_low_klines;
    use crate::qtrade::strategy::util::highlow_klines::find_rank;
    use crate::qtrade::strategy::util::highlow_klines::percentage_deviation_from_mean;
    use crate::qtrade::strategy::util::highlow_klines::compute_securities_kline_deviations;
    use crate::qtrade::strategy::util::highlow_klines::standard_deviation;
    use crate::qtrade::strategy::util::highlow_klines::variance;
    use crate::qtrade::strategy::util::highlow_klines::mean;
    use crate::todo::MAX_KLINE_DAYS;
    use crate::todo::MAX_STK_SECURITY;

    #[tokio::test]
    async fn test_stats() {
        // Initialize the logger for the test context.
        // pretty_env_logger::try_init().unwrap();
        pretty_env_logger::init_timed();

        let numbers = vec![3.0, 5.0, 6.0, 4.8, 6.0, 4.0, 4.3, 2.0, 6.0, 8.0];
        let given_number = 3.3;

        let mean_val = mean(&numbers);
        let var = variance(&numbers);
        let std_deviation = standard_deviation(&numbers);
        let abs_deviation = absolute_deviation_from_mean(given_number, &numbers);
        let pct_deviation = percentage_deviation_from_mean(given_number, &numbers);

        info!("Numbers: {:?}", numbers);
        info!("Mean: {:.2}", mean_val);
        info!("Variance: {:.2}", var);
        info!("Standard Deviation: {:.2}", std_deviation);
        info!("Absolute Deviation of {}: {:.2}", given_number, abs_deviation);
        info!("Percentage Deviation of {}: {:.2}", given_number, pct_deviation);
    }

    #[tokio::test]
    async fn test_calculate_latest_klines_deviation() {
        println!("长线抄底模式，36个月内以周线最低价为基准");
        let _ = db::init().await.unwrap();

        let security_id = "SZ.STK.300724"; //"SZ.ETF.159857";//"SH.ETF.512480";//"SH.ETF.512400";//

        let kldevs = calculate_latest_klines_deviation(
            security_id.to_string(),
            Klt::DAY,
            80,
            &(0, 36 * 31),
            Klt::WEEK,
            10,
            "1".to_string()
        ).await.unwrap();
        for (idx, kld) in kldevs.iter().enumerate() {
            println!(
                "[{:02}] dev:{:.02} rank:{:.02} low:{:.02}~mean:{:.02} llows:{:?}",
                idx,
                kld.pct_deviation,
                kld.rank,
                kld.ohlcv.l,
                kld.mean,
                kld.lowest_lows
            );
        }

        println!("\n中线抄底模式，18个月内以日线最低价为基准");

        let kldevs = calculate_latest_klines_deviation(
            security_id.to_string(),
            Klt::DAY,
            80,
            &(0, 18 * 31),
            Klt::WEEK,
            10,
            "2".to_string()
        ).await.unwrap();
        for (idx, kld) in kldevs.iter().enumerate() {
            println!(
                "[{:02}] dev:{:.02} rank:{:.02} low:{:.02}~mean:{:.02} llows:{:?}",
                idx,
                kld.pct_deviation,
                kld.rank,
                kld.ohlcv.l,
                kld.mean,
                kld.lowest_lows
            );
        }

        println!("\n短线抄底模式，9个月内以日线的最低价为基准");

        let kldevs = calculate_latest_klines_deviation(
            security_id.to_string(),
            Klt::DAY,
            80,
            &(0, 9 * 31),
            Klt::DAY,
            10,
            "3".to_string()
        ).await.unwrap();
        for (idx, kld) in kldevs.iter().enumerate() {
            println!(
                "[{:02}] dev:{:.02} rank:{:.02} low:{:.02}~mean:{:.02} llows:{:?}",
                idx,
                kld.pct_deviation,
                kld.rank,
                kld.ohlcv.l,
                kld.mean,
                kld.lowest_lows
            );
        }

        println!("\n短线抄底模式，6个月内以60分钟线的最低价为基准");

        let kldevs = calculate_latest_klines_deviation(
            security_id.to_string(),
            Klt::M060,
            80,
            &(0, 6 * 31),
            Klt::DAY,
            10,
            "4".to_string()
        ).await.unwrap();
        for (idx, kld) in kldevs.iter().enumerate() {
            println!(
                "[{:02}] dev:{:.02} rank:{:.02} low:{:.02}~mean:{:.02} llows:{:?}",
                idx,
                kld.pct_deviation,
                kld.rank,
                kld.ohlcv.l,
                kld.mean,
                kld.lowest_lows
            );
        }
    }

    #[tokio::test]
    async fn test_compute_securities_kline_deviations() {
        // Initialize the logger for the test context.
        // pretty_env_logger::try_init().unwrap();
        pretty_env_logger::init_timed();
        let _ = db::init().await.unwrap();

        let count = compute_securities_kline_deviations(
            &None,
            Some(SecurityType::ETF),
            Klt::DAY,
            MAX_KLINE_DAYS,
            &(0, 3 * 365),
            Klt::WEEK,
            10,
            0,
            10 // MAX_STK_SECURITY
        ).await.unwrap();

        info!(" save {count} KlineDeviations into db");
    }

    #[tokio::test]
    async fn test_find_rank() {
        let mut values: Vec<f64> = vec![10.0, 20.0, 30.0, 40.0, 50.0];
        let target: f64 = 25.0;
        let rank = find_rank(&mut values, target);
        println!("The rank of {} is: {}", target, rank);
        assert_eq!(rank, 2, "The rank of 25.0 should be 2");

        // Test for target less than all values
        assert!(find_rank(&mut values, 1.0) <= 0, "The rank of 1.0 should be 0 or negative");

        // Test for target greater than all values
        assert_eq!(find_rank(&mut values, 100.0), i32::MAX, "The rank of 100.0 should be i32::MAX");
    }

    /// high deviation
    pub struct HighDev {
        pub high: Kline,
        pub close_dev: f64,
        pub day_dev: i64,
    }

    /// low deviation
    pub struct LowDev {
        pub kline: Kline,
        pub close_dev: f64,
        pub day_dev: i64,
    }

    /// Computes the ratio of items in the vector where the `kline.low` is greater than the given value.
    ///
    /// # Arguments
    /// * `low_devs` - A vector of `LowDev` structs.
    /// * `threshold` - The threshold `kline.low` value.
    ///
    /// # Returns
    /// * The ratio of items in the vector where the threshold is greater than `kline.low`.
    /// * ratio 值越小表示越接近最低
    ///
    pub fn compute_low_ratio(low_devs: &Vec<LowDev>, threshold: f64) -> f64 {
        let count_greater = low_devs
            .iter()
            .filter(|ld| ld.kline.low < threshold)
            .count();
        let total_count = low_devs.len();

        if total_count == 0 {
            0.0
        } else {
            round2(((count_greater as f64) / (total_count as f64)) * 100.0)
        }
    }

    /// Counts the number of LowDev instances where close_dev is greater than 0 and less than 10.
    fn count_close_low_dev_in_range(low_devs: &Vec<LowDev>, min: f64, max: f64) -> usize {
        low_devs
            .iter()
            .filter(|ld| ld.close_dev > min && ld.close_dev < max)
            .count()
    }

    /// Counts the number of HighDev instances where close_dev is greater than 0 and less than 10.
    fn count_close_high_dev_in_range(high_devs: &Vec<HighDev>, min: f64, max: f64) -> usize {
        high_devs
            .iter()
            .filter(|ld| ld.close_dev > min && ld.close_dev < max)
            .count()
    }

    #[tokio::test]
    async fn test_find_high_low_klines() {
        // Initialize the logger for the test context.
        // pretty_env_logger::try_init().unwrap();
        pretty_env_logger::init_timed();
        let _ = db::init().await.unwrap();

        let now = Utc::now();
        // 获取前一天的日期时间
        let day_before = now - Duration::days(1250);
        println!(" day_before from {}", day_before);

        let all_klines = db::kline
            ::select("SH.STK.600839", None, Some(&Klt::DAY), Some("klts DESC"), 0, 250).await
            .unwrap();
        println!("select oldest kline={:?}", all_klines.last());

        if let Some(curr_kl) = all_klines.get(0) {
            println!("curr_kl {:?}\n", curr_kl);

            // Example usage:
            let period = 20;
            let hlpairs = find_high_low_klines(&all_klines, &curr_kl.klts, period);
            println!("find_high_low_klines= {:?}", hlpairs);

            let mut highdevs = Vec::new();
            let mut lowdevs = Vec::new();
            for (idx, hlpair) in hlpairs.iter().enumerate() {
                let hd = HighDev {
                    high: hlpair.0.clone(),
                    close_dev: round2(
                        ((curr_kl.ohlcv().c - hlpair.0.ohlcv().h) / curr_kl.ohlcv().c) * 100.0
                    ),
                    day_dev: curr_kl.klts.signed_duration_since(hlpair.0.klts).num_weeks(),
                };
                highdevs.push(hd);

                println!(
                    "[{idx}]H, {} {} {:03.2}% {:03}",
                    hlpair.0.id_raw(),
                    hlpair.0.ohlcv().h,
                    round2(((curr_kl.ohlcv().c - hlpair.0.ohlcv().h) / curr_kl.ohlcv().c) * 100.0),
                    curr_kl.klts.signed_duration_since(hlpair.0.klts).num_weeks()
                );

                let lw = LowDev {
                    kline: hlpair.1.clone(),
                    close_dev: round2(
                        ((curr_kl.ohlcv().c - hlpair.1.ohlcv().l) / curr_kl.ohlcv().c) * 100.0
                    ),
                    day_dev: curr_kl.klts.signed_duration_since(hlpair.1.klts).num_weeks(),
                };
                lowdevs.push(lw);
                println!(
                    "[{idx}]L, {} {} {:03.2}% {:03}",
                    hlpair.1.id_raw(),
                    hlpair.1.ohlcv().l,
                    round2(((curr_kl.ohlcv().c - hlpair.1.ohlcv().l) / curr_kl.ohlcv().c) * 100.0),
                    curr_kl.klts.signed_duration_since(hlpair.1.klts).num_weeks()
                );
            }
            highdevs.sort_by(|a, b|
                b.close_dev
                    .partial_cmp(&a.close_dev)
                    .unwrap_or(std::cmp::Ordering::Equal)
                    .then_with(||
                        b.high
                            .ohlcv()
                            .h.partial_cmp(&a.high.ohlcv().h)
                            .unwrap_or(std::cmp::Ordering::Equal)
                    )
            );

            let count = count_close_high_dev_in_range(&highdevs, 0.0, 2.0);
            println!("High sorted, (0.0,0.1): {count}");
            for (idx, hd) in highdevs.iter().enumerate() {
                println!(
                    "[{idx}]H, {} {} {:03.2}% {:03}\n",
                    hd.high.id_raw(),
                    hd.high.ohlcv().h,
                    hd.close_dev,
                    hd.day_dev
                );
            }
            println!("High sorted, (0.0,2.0): {count}");

            println!("\n\n*************lowlow****************");
            lowdevs.sort_by(|a, b|
                a.kline
                    .ohlcv()
                    .l.partial_cmp(&b.kline.ohlcv().l)
                    .unwrap_or(std::cmp::Ordering::Equal)
                    .then_with(||
                        a.close_dev.partial_cmp(&b.close_dev).unwrap_or(std::cmp::Ordering::Equal)
                    )
            );
            let count = count_close_low_dev_in_range(&lowdevs, 0.0, 5.0);
            let ratio = compute_low_ratio(&lowdevs, 4.13);
            println!("Low sorted, (0.0,5.0): {count}:{ratio}%");
            for (idx, lw) in lowdevs.iter().enumerate() {
                println!(
                    "[{idx}]L, {} {} {:03.2}% {:03}",
                    lw.kline.id_raw(),
                    lw.kline.ohlcv().l,
                    lw.close_dev,
                    lw.day_dev
                );
            }
            println!(
                "Low sorted, (0.0,5.0): {count}:{ratio}% , curr_kl={} ohlcv={:?}",
                curr_kl.id_raw(),
                curr_kl.ohlcv()
            );
        }
    }

    #[tokio::test]
    async fn test_ain() {
        let f = 2.3;
        print!("{:.02}", f); // Output: 02.30
    }
}
